import Log from '@/utils/logger';
import config from '@/config';
import functions from '../modules/functions';
import matlab from '../modules/matlab'; // 配置matlab模块存储loaclStorage
import user from '../modules/user'; // 配置user模块储存localStorage

/**
 * Vuex plugin for save and sync 'user' from vuex modules.
 */
class SyncStorage {
  constructor(options) {
    /** init options */
    this.options = options;
    this.storage = (window && window[options.storage]) || (window && window[config.storage]);
    this.prefix = options.prefix || config.prefix;
    this.ttl = options.ttl || config.ttl;
    this.user = 'user';
    this.functions = 'functions';
    this.matlab = 'matlab';

    this.matlabMutations = this.getModuleOptions(matlab, 'mutations');
    this.userMutations = this.getModuleOptions(user, 'mutations');
    this.functionsMutations = this.getModuleOptions(functions, 'mutations');

    // Log.log('[SyncStorage.constructor] ->', this);
  }

  /**
   * Vuex subscribe plugin function.
   * @param {Object} store Vuex instance
   */
  subscribe = async(store) => {
    if (!this.checkStorage()) {
      throw new Error('[SyncStorage] Invalid "Storage" instance given');
    }

    if (this.checkVersion(config.version)) {
      Log.log(`[SyncStorage.subscribe] Current version of the application "${config.version}"`);
    } else {
      Log.warn(`[SyncStorage.subscribe] Application version updated to "${config.version}"`);
    }

    // init and apply functions state from storage
    if (this.initState('functions', store)) {
      Log.log('[SyncStorage.subscribe] init functions success');
    } else {
      Log.warn('[SyncStorage.subscribe] No functions state in storage');
    }
    if (this.initState('matlab', store)) {
      Log.log('[SyncStorage.subscribe] init matlab success');
    } else {
      Log.warn('[SyncStorage.subscribe] No matlab state in storage');
    }
    if (this.initState('user', store)) {
      Log.log('[SyncStorage.subscribe] init user success');
    } else {
      Log.warn('[SyncStorage.subscribe] No user state in storage');
    }

    store.subscribe((mutation, state) => {
      if (mutation.type.indexOf(this.matlab) > -1) { // matlab
        const name = mutation.type.replace(this.matlab + '/', '');
        if (this.matlabMutations.includes(name)) {
          this.setToStorage(`${this.prefix}${this.matlab}`, JSON.stringify(state.matlab));
        }
      } else if (mutation.type.indexOf(this.user) > -1) { // user
        const name = mutation.type.replace(this.user + '/', '');
        if (this.userMutations.includes(name)) {
          this.setToStorage(`${this.prefix}${this.user}`, JSON.stringify(state.user));
        }
      } else if (mutation.type.indexOf(this.functions) > -1) { // user
        const name = mutation.type.replace(this.functions + '/', '');
        if (this.functionsMutations.includes(name)) {
          this.setToStorage(`${this.prefix}${this.functions}`, JSON.stringify(state.functions));
        }
      }
    });
  };

  /**
   * Get current seconds + ttl.
   * @param {Number} [ttl] Session lifetime
   */
  getSeconds = (ttl) => Math.floor(Date.now() / 1000) + (ttl || 0);

  /**
   * Get array of module mutation types.
   */
  getModuleOptions = (module, key) => {
    if (!module || !module[key]) return [];
    return Object.keys(module[key]);
  };

  /**
   * Check LocalStorage to read/write.
   */
  checkStorage() {
    try {
      this.storage.setItem(`${this.prefix}@@`, '1');
      this.storage.removeItem(`${this.prefix}@@`);
      return true;
    } catch (e) {
      Log.error('[SyncStorage.checkStorage]', e);
      return false;
    }
  }

  /**
   * Check application version.
   * If the version of the application has changed, then the storage is cleared of the session
   * and settings. Required to apply the settings of the new version of the application
   * @param {String} version version of the application
   */
  checkVersion(version) {
    try {
      if (this.getFromStorage(`${this.prefix}version`) === version) {
        return true;
      }

      this.storage.clear();
      this.setToStorage(`${this.prefix}version`, version);
    } catch (e) {
      Log.error('[SyncStorage.checkVersion]', e);
    }
    return false;
  }

  /**
   * Get data info from storage.
   */
  initState(type, store) {
    const state = this.getFromStorage(`${this.prefix}${this[type]}`);
    if (state) {
      const data = JSON.parse(state);
      store.commit(type + '/SET_DATA', data);
      return true;
    }
    return false;
  }

  /**
   * Get data to storage.
   * @param {String} key
   * @param {String} value
   */
  setToStorage(key, value) {
    try {
      this.storage.setItem(key, value);
      return true;
    } catch (e) {
      Log.error('[SyncStorage.setToStorage]', e);
      return false;
    }
  }

  /**
   * Get data from storage.
   * @param {string} key
   * @return string | null
   */
  getFromStorage(key) {
    try {
      const data = this.storage.getItem(key);
      return data;
    } catch (e) {
      Log.error('[SyncStorage.getFromStorage]', e);
      return '';
    }
  }
}

export default function(syncStorageOption) {
  return new SyncStorage(syncStorageOption).subscribe;
}
